//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<pair.h>
///	@path	~/src/lib/math/
///	@date	2007/09/16
///	@desc	Pair, with 2 values packed for 2D coordinates.

#pragma once

#include "xeres.h"

#include "lib/math/math_ext.h"

namespace xeres {

	/*!
		\class		Pair
		\brief		For 2 floats pair coordinates use.
	*/
	template<
		typename _Ty
	> class Pair : public ArithmeticOp< _Ty >
	{
	public:

		// scalar type
		typedef _Ty _Scalar;

		// data
		union
		{
			// coordinate system access
			struct { _Scalar x , y; };

			// array access
			_Scalar f[2];
		};

		// dimension ( accessible member )
		static const size_t DIMENSION = 2;

	public:
		// default ctor
		Pair( void ) : x( 0 ) , y( 0 )
		{
		}
		// set ctor
		Pair( const _Scalar sx , const _Scalar sy ) : x( sx ) , y( sy )
		{
		}
		// copy ctor
		Pair( const Pair& v ) : x( v.x ) , y( v.y )
		{
		}
		// copy ctor
		template< typename _TOther > Pair( const Pair<_TOther>& val )
			: x( _Scalar(val.x) ) , y( _Scalar(val.y) )
		{
		}
		// additional set ctor
		explicit Pair( const _Scalar s ) : x( s ) , y( s )
		{
		}
		// set from 2 any-type-values
		void set( const _Scalar sx , const _Scalar sy )
		{
			x = sx;
			y = sy;
		}
		// set from a data collection with subscription
		template<typename _TArray> void from_array( const _TArray& sf )
		{
			x = _Scalar(sf[0]);
			y = _Scalar(sf[1]);
		}
		// set to zero
		void set_zero( void )
		{
			x = y = 0;
		}
		// set from single value
		void single( const _Scalar s )
		{
			x = y = s;
		}
		// memory copy from a floating point array
		void copy( const _Scalar * s )
		{
			memcpy( f , s , sizeof(_Scalar) * DIMENSION );
		}
		// square sum of 2 components
		_Scalar square( void ) const
		{
			return x * x + y * y;
		}
		// length of pair
		_Scalar length( void ) const
		{
			return sqrt( square() );
		}
		// normalize vector
		void normalize( void )
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			_Scalar len = x * x + y * y;
			if( len > 0 )
			{
				len = recip_sqrt( len );
				x *= len;
				y *= len;
			}
		}
		// normalize & return value
		Pair<_Scalar> normalized( void ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			_Scalar len = x * x + y * y;
			if( len > 0 )
			{
				len = recip_sqrt( len );
				return Pair( x * len , y * len );
			}
			return Pair( 0 );
		}
		// calculate the distance from this vector to another
		template< typename _TOther > _Scalar distance( const Pair<_TOther>& to ) const
		{
			_Scalar a = x - _Scalar(to.x);
			_Scalar b = y - _Scalar(to.y);
			return sqrt( a*a + b*b );
		}
		// calculate the dot product
		template<typename _TOther> _Scalar dot( const Pair<_TOther>& to ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			TYPE_MUST_BE_FLOAT( _TOther );
			return x * _Scalar(to.x) + y * _Scalar(to.y);
		}
		// calculate the cross product
		template<typename _T1 , typename _T2> void cross( const Pair<_T1>& a , const Pair<_T2>& b )
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			TYPE_MUST_BE_FLOAT( _T1 );
			TYPE_MUST_BE_FLOAT( _T2 );
			y = x = ( _Scalar(a[AXIS_X]) * _Scalar(b[AXIS_Y]) - _Scalar(a[AXIS_Y]) * _Scalar(b[AXIS_X]) );
		}
		// theta of vector
		_Scalar theta( void ) const
		{
			return atan( y / x );
		}
		// comparison
		friend bool operator == ( const Pair<_Scalar>& a , const Pair<_Scalar>& b )
		{
			return ( a.x == b.x ) && ( a.y == b.y );
		}
		friend bool operator != ( const Pair<_Scalar>& a , const Pair<_Scalar>& b )
		{
			return ( a.x != b.x ) || ( a.y != b.y );
		}
		// subscription access
		_Scalar& operator [] ( const int index )
		{
			CHECK_INDEX( index , DIMENSION );
			return f[index];
		}
		const _Scalar& operator [] ( const int index ) const
		{
			CHECK_INDEX( index , DIMENSION );
			return f[index];
		}
		// assignment
		template< typename _TOther > Pair<_Scalar>& operator = ( const Pair<_TOther>& a )
		{
			f[0] = _Scalar(a.f[0]);
			f[1] = _Scalar(a.f[1]);
			return *this;
		}
		const Pair& operator = ( const _Scalar a )
		{
			x = y = a;
			return *this;
		}
		// operators
		friend Pair operator - ( const Pair& op )
		{
			return Pair( -op.x , -op.y );
		}
		const Pair& operator += ( const Pair& op )
		{
			x += op.x;
			y += op.y;
			return *this;
		}
		const Pair& operator += ( const _Scalar op )
		{
			x += op;
			y += op;
			return *this;
		}
		const Pair& operator -= ( const Pair& op )
		{
			x -= op.x;
			y -= op.y;
			return *this;
		}
		const Pair& operator -= ( const _Scalar op )
		{
			x -= op;
			y -= op;
			return *this;
		}
		const Pair& operator *= ( const Pair& op )
		{
			x *= op.x;
			y *= op.y;
			return *this;
		}
		const Pair& operator *= ( const _Scalar op )
		{
			x *= op;
			y *= op;
			return *this;
		}
		const Pair& operator /= ( const Pair& op )
		{
			x /= op.x;
			y /= op.y;
			return *this;
		}
		const Pair<_Scalar>&operator /= ( const _Scalar op )
		{
			x /= op;
			y /= op;
			return *this;
		}

		// outside operators
		friend Pair operator + ( const Pair& a , const Pair& b )
		{
			return Pair( a.x + b.x , a.y + b.y );
		}
		friend Pair operator + ( const Pair& a , const _Scalar op )
		{
			return Pair( a.x + op , a.y + op );
		}
		friend Pair operator - ( const Pair& a , const Pair& b )
		{
			return Pair( a.x - b.x , a.y - b.y );
		}
		friend Pair operator - ( const Pair& a , const _Scalar op )
		{
			return Pair( a.x - op , a.y - op );
		}
		friend Pair operator * ( const Pair& a , const Pair& b )
		{
			return Pair( a.x * b.x , a.y * b.y );
		}
		friend Pair operator * ( const Pair& a , const _Scalar op )
		{
			return Pair( a.x * op , a.y * op );
		}
		friend Pair operator / ( const Pair& a , const Pair& b )
		{
			return Pair( a.x / b.x , a.y / b.y );
		}
		friend Pair operator / ( const Pair& a , const _Scalar op )
		{
			return Pair( a.x / op , a.y / op );
		}
	};

	// each component be the max of 2 input values.
	inline Pair<float> max( const Pair<float>& a , const Pair<float>& b )
	{
		return Pair<float>( max( a.x , b.x ) , max( a.y , b.y ) );
	}
	// each component be the min of 2 input values.
	inline Pair<float> min( const Pair<float>& a , const Pair<float>& b )
	{
		return Pair<float>( min( a.x , b.x ) , min( a.y , b.y ) );
	}
	// each component be the max of 2 input values.
	inline Pair<int> max( const Pair<int>& a , const Pair<int>& b )
	{
		return Pair<int>( max( a.x , b.x ) , max( a.y , b.y ) );
	}
	// each component be the min of 2 input values.
	inline Pair<int> min( const Pair<int>& a , const Pair<int>& b )
	{
		return Pair<int>( min( a.x , b.x ) , min( a.y , b.y ) );
	}
	// comparison with epsilon
	template<
		typename _Scalar
	> inline bool is_equal( const Pair<_Scalar>& a , const Pair<_Scalar>& b )
	{
		return ( is_equal( a.x , b.x ) && is_equal( a.y , b.y ) );
	}
	template<
		typename _Scalar
	> inline Pair<_Scalar> clamp( const Pair<_Scalar>& value , const Pair<_Scalar>& minimum , const Pair<_Scalar>& maximum ) {
		return Pair<float>( clamp( value.x , minimum.x , maximum.x ) , clamp( value.y , minimum.y , maximum.y ) );
	}
	template<
		typename _Scalar
	> inline void clamp_ref( Pair<_Scalar>& value , const Pair<_Scalar>& minimum , const Pair<_Scalar>& maximum ) {
		clamp_ref( value.x , minimum.x , maximum.x );
		clamp_ref( value.y , minimum.y , maximum.y );
	}
	template<
		typename _Scalar
	> inline _Scalar length( const Pair<_Scalar>& val ) {
		return val.length();
	}
	template<
		typename _Scalar
	> inline Pair<_Scalar> cross( const Pair<_Scalar>& a , const Pair<_Scalar>& b )
	{
		Pair<_Scalar> ret;
		ret.cross( a , b );
		return ret;
	}
	template<
		typename _Scalar
	> inline _Scalar distance( const Pair<_Scalar>& a , const Pair<_Scalar>& b )
	{
		return a.distance( b );
	}

} // namespace xeres
